import Stats from 'stats-gl';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/Addons.js';
import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer.js'; // 引入CSS2DRenderer
import { CSS3DRenderer } from 'three/examples/jsm/Addons.js';
export default class {
  options: any;
  scene: THREE.Scene;
  control: OrbitControls;
  cssRenderer: CSS2DRenderer | CSS3DRenderer;
  stats?: any;
  renderer: THREE.WebGLRenderer;
  camera: THREE.PerspectiveCamera;
  constructor(options = {}) {
    this.options = options;
    this.createScene();
  }
  createScene() {
    const { renderer = {}, control = {}, camera = {}, css2D, css3D } = this.options;
    const { width, height, aspect, pixelRatio } = this.windowAspect();
    const { target = {}, ...controlOptions } = control;
    const { antialias, alpha, ...rendererOptions } = renderer;
    const { fov, far, near, position = {}, ...camerOptions } = camera;

    // 初始化场景
    this.scene = new THREE.Scene();
    for (const key in this.options) {
      const isKey = ['background', 'environment', 'fog', 'overrideMaterial'].includes(key);
      if (isKey) this.scene[key] = this.options[key];
    }
    // 初始渲染器
    this.renderer = new THREE.WebGLRenderer({ antialias, alpha });
    this.renderer.setPixelRatio(pixelRatio);
    this.renderer.setSize(width, height);
    for (const key in rendererOptions) {
      this.renderer[key] = rendererOptions[key];
    }
    if (css2D || css3D) {
      this.cssRenderer = css3D ? new CSS3DRenderer() : new CSS2DRenderer();
      this.cssRenderer.setSize(width, height);
      this.cssRenderer.domElement.style.top = '0px';
      this.cssRenderer.domElement.style.position = 'absolute';
    }
    // 初始化相机
    this.camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
    for (const key in camerOptions) {
      this.camera[key] = camerOptions[key];
    }
    // 初始化控制器
    this.control = new OrbitControls(
      this.camera,
      this.cssRenderer?.domElement || this.renderer.domElement
    );
    for (const key in controlOptions) {
      this.control[key] = controlOptions[key];
    }
    if (position) {
      for (const key in position) {
        const name = Array.isArray(position) ? ['x', 'y', 'z'][key] : key;
        this.camera.position[name] = position[key];
      }
    }
    if (target) {
      for (const key in target) {
        const name = Array.isArray(position) ? ['x', 'y', 'z'][key] : key;
        this.control.target[name] = target[key];
      }
    }
  }
  bind(str) {
    const dom = str.value || document.querySelector(str);
    dom?.appendChild(this.renderer.domElement);
    if (this.options.stats) {
      this.stats = new Stats({
        trackGPU: true,
        horizontal: false
      });
      this.stats.init(this.renderer);
      dom?.appendChild(this.stats.dom);
    }
    if (this.cssRenderer) {
      dom?.appendChild(this.cssRenderer.domElement);
    }
    window.addEventListener('resize', this.windowResize.bind(this), false);
  }
  add(obj, groupkey = 'scene') {
    this[groupkey].add(obj);
  }
  update() {
    this.stats?.update();
    this.control?.update();
    this.renderer.render(this.scene, this.camera);
    if (this.cssRenderer) {
      this.cssRenderer.render(this.scene, this.camera);
    }
  }
  windowAspect() {
    let width = window.innerWidth;
    let height = window.innerHeight;
    let pixelRatio = window.devicePixelRatio;
    if (this.options.width && this.options.height) {
      width = this.options.width;
      height = this.options.height;
    }
    return { width, height, pixelRatio, aspect: width / height };
  }
  windowResize() {
    const { width, height, aspect } = this.windowAspect();
    this.camera.aspect = aspect;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(width, height);
    if (this.cssRenderer) {
      this.cssRenderer.setSize(width, height);
    }
  }
}
